
#include "packet-pool.h"
#include "ip.h"
#include "tcp.h"
#include <iostream>
#include <iomanip>
using namespace std;
PacketPool::PacketPool() {
    pkt_table_ = NULL;
    b_expired_ = true;
    b_first_ = true;
    pre_pkt_ = 0;
    next_pkt_ = 0;
}

PacketPool::~PacketPool() {
    pkt_entry_t *current, *temp;
    HASH_ITER(hh, pkt_table_, current, temp) {
	HASH_DEL(pkt_table_, current);
	free(current);
    }

    report_list_.clear();

    // should free the Packet* first??
    // should there be other iterator for temporary storage??
    pkt_iter_t it = backup_queue_.begin();
    for(; it != backup_queue_.end(); ++it) {
	Packet::free(*it);
    }
    //backup_queue_.clear();
}

/*
 * 1. check whether the packet has been in the pool
 * 2. fistly, add this packet into the backup-queue
 * 3. secondly, add this packet's info into the hash-table
 * 4. finally, add this packet's info into the reception report list
 */
void PacketPool::in_pool(Packet* pkt) {

    // if it's an encoded pkt, just ignore it
    if((int)HDR_COPE(pkt)->encoded_num_ > 0)
	return;

    // 1. check whether the packet has been in the pool
    pkt_entry_t *result;
    int x = HDR_CMN(pkt)->cope_pkt_id();
    HASH_FIND_INT(pkt_table_, &x, result);
    if(result != NULL) {
  	return;
    }

    hdr_cmn* hc = HDR_CMN(pkt);
    u_int16_t ipseq = hc->cope_seq_num();
    nsaddr_t srcip = HDR_IP(pkt)->saddr();
    report_iter_t it = lookup_node(srcip);

    // incoming packet has been garbage collected, igonre it
    if(it != report_list_.end() && ipseq <= it->shifts)
         return;
 
    Packet * p = pkt->copy();
  
    // 2. fistly, add this packet into the backup-queue
    //    Packet* new_pkt = p->copy(); // don't forget to free it
    backup_queue_.push_back(p);
  
    // 3. secondly, add this packet's info into the hash-table
    pkt_entry_t *new_entry = (pkt_entry_t*)malloc(sizeof(pkt_entry_t));
    memset(new_entry, 0, sizeof(pkt_entry_t));
    new_entry->pktid = hc->cope_pkt_id();
    new_entry->ppkt = p;
    HASH_ADD_INT(pkt_table_, pktid, new_entry);

 
    // 4. finally, add this packet's info into the reception report list

    // check whether this src_ip has been in the report list or not
    report_t rpt;
    int pktid = hc->cope_pkt_id();
    if(it == report_list_.end()) { // not exist
	rpt.srcip = srcip;
	rpt.lastpkt = ipseq;
	rpt.gc = false;
	rpt.shifts = 0;
	rpt.pre = 0;
	rpt.next = 0;
	/*
	 * This ip seq number is different from local seq number, because
	 * the first received ip seq number of this node maybe is already
	 * bigger than 256, so we sould get the modulo of it.
	 */
	rpt.bitmap.set(BITMAP_TOTAL_SIZE - ((ipseq - 1) % BITMAP_TOTAL_SIZE) - 1);
	if(b_expired_ == true) {
	    pre_pkt_ = pktid;
	    rpt.pre = ipseq;
	} else {
	    next_pkt_ = pktid;
	    rpt.next = ipseq;
	}
	report_list_.push_back(rpt);
    } else { // this node exists in the report list
	it->lastpkt = it->lastpkt > ipseq ? it->lastpkt : ipseq;
	if(b_expired_ == true) {
	    it->pre = it->pre > ipseq ? it->pre : ipseq;
	    pre_pkt_ = pktid;
	} else {
	    it->next = it->next > ipseq ? it->next : ipseq;
	    next_pkt_ = pktid;
	}
    }
 }

void PacketPool::delete_pkt_ht(int pktid) {
    pkt_entry_t* entry;
    HASH_FIND_INT(pkt_table_, &pktid, entry);
    if(entry != NULL) {
	HASH_DEL(pkt_table_, entry);
	free(entry);
    }
}

Packet* PacketPool::get_packet(int pktid) {
    pkt_entry_t* result;
    HASH_FIND_INT(pkt_table_, &pktid, result);
    if(result != NULL) 
      return result->ppkt;
    return NULL;
}

/*
 * 1. first delete the report-list part, if has not done before
 * 2. second delete the hash-table part
 * 3. finally delete the backup-queue part
 * 4. reset the flags
 */
void PacketPool::garbage_collection() {
    if(backup_queue_.empty()) {
	b_expired_ = !b_expired_;
	return;
    }

    if(pre_pkt_ == 0 || next_pkt_ == 0) {
	b_expired_ = !b_expired_;
	b_first_ = false;
	return;
    }

    // cout << "-------------------before---------------" << endl;
    // showPool();
    
    Packet* tmp;
    int id;
    int lastone;
    if(b_expired_ == false)
	lastone = pre_pkt_;
    else
	lastone = next_pkt_;

    pkt_iter_t it_bq = backup_queue_.begin();
    // for( ; it_bq != backup_queue_.end(); it_bq ++) {
    // 	printf(" <<<packet-id: %d, lastone: %d\n", HDR_CMN(*it_bq)->cope_pkt_id(), lastone);
    // }
    it_bq = backup_queue_.begin();
    while(it_bq != backup_queue_.end()) {
	tmp = *it_bq;
	hdr_cmn* hc = HDR_CMN(tmp);
	id = hc->cope_pkt_id();

	// 1. first delete the report-list part, if has not done before
	nsaddr_t srcip = HDR_IP(tmp)->saddr();
	report_iter_t it = lookup_node(srcip);
	if(it->gc == false) { // if this node has not done garbage collection
	    // assume the bitmap is big enough to hold several periods
	    if(b_expired_ == false) { // delete the previous period packets
		// printf(" shifts: %d \n", it->shifts);
		it->bitmap <<= (it->pre - it->shifts) % BITMAP_TOTAL_SIZE;
		it->shifts += (it->pre - it->shifts) % BITMAP_TOTAL_SIZE;
		// printf(" srcip: %d, pre: %d, shifts: %d--------------\n", it->srcip, it->pre, it->shifts);
	    } else {
		// printf(" shifts: %d \n", it->shifts);
		it->bitmap <<= (it->next - it->shifts) % BITMAP_TOTAL_SIZE;
		it->shifts += (it->next - it->shifts) % BITMAP_TOTAL_SIZE;
		// printf(" srcip: %d, next: %d, shifts: %d--------------\n", it->srcip, it->next, it->shifts);
	    }
	    it->gc = true;	    
	}
	
	// 2. second delete the hash-table part
	delete_pkt_ht(id);

	// 3. finally delete the backup-queue part
	Packet::free(*it_bq);
	it_bq = backup_queue_.erase(it_bq);

	if(id == lastone)
	    break;
    }

    // 4.1 reset the expired flag
    b_expired_ = !b_expired_;

    // 4.2 reset each node's gc flag
    report_iter_t it = report_list_.begin();
    for(; it != report_list_.end(); ++it) {
	it->gc = false;
    }

    // cout << "-------------------after-----------------" << endl;
    // showPool();

}

report_iter_t PacketPool::lookup_node(nsaddr_t srcip) {
    report_iter_t it = report_list_.begin();
    for(; it != report_list_.end(); ++it) {
	if(it->srcip == srcip) {
	    return it;
	}
    }
    return it;
}

/*
 * 1. if that node does not exist in the pool, return NULL
 * 2. get the report
 */
hdr_report_t* PacketPool::get_report(nsaddr_t srcip) {
    // check whether this node does exist in the pool
    report_iter_t it = lookup_node(srcip);
    if(it == report_list_.end()) {
	return NULL;
    }
    
    hdr_report_t* rpt = (hdr_report_t*)malloc(sizeof(hdr_report_t));

    rpt->src_ip_ = srcip;
    rpt->last_pkt_ = it->lastpkt;
    //u_int8_t x = it->shifts;
    u_int8_t lastpkt_mod = it->lastpkt % BITMAP_TOTAL_SIZE;
    u_int8_t shifts_mod = it->shifts % BITMAP_TOTAL_SIZE;

    // if the lastpkt_mod < shifts_mod, then lastpkt > 256, while shifts < 256
    if(lastpkt_mod < shifts_mod) {
	if(lastpkt_mod == 0) {
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 BITMAP_TOTAL_SIZE - BITMAP_SIZE - 1,
					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();	    
	} else if(lastpkt_mod > 0 && lastpkt_mod <= BITMAP_SIZE) {
	    // the bitmap has two (three) parts

	    u_int8_t end_num = BITMAP_SIZE + 1 - lastpkt_mod;
	    // get the end part
	    bitset<BITMAP_SIZE> temp_end(it->bitmap.to_string(),
					 BITMAP_TOTAL_SIZE - end_num,
					 end_num);
	    // shifts lastpkt_mod - 1 bits to head part
	    temp_end <<= lastpkt_mod - 1;

	    // get the head part
	    bitset<BITMAP_SIZE> temp_head(it->bitmap.to_string(),
					  0,
					  lastpkt_mod - 1);
	    // get the OR of two bitmap
	    temp_end |= temp_head;
	    rpt->bit_map_ = temp_end.to_ulong();
	} else { // get the latest bits
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 lastpkt_mod - BITMAP_SIZE - 1,
					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();
	}
    } else { // shifts_mod < lastpkt_mod < 256
	if(lastpkt_mod - shifts_mod <= BITMAP_SIZE) { // bitmap has two parts
	    if(lastpkt_mod - shifts_mod - 1 == 0)
		rpt->bit_map_ = 0;
	    else {
		bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					     0,
					     lastpkt_mod - shifts_mod - 1);
		rpt->bit_map_ = temp_map.to_ulong();
	    }
	} else { // get the latest bits
	    bitset<BITMAP_SIZE> temp_map(it->bitmap.to_string(),
					 lastpkt_mod - BITMAP_SIZE - 1,
					 BITMAP_SIZE);
	    rpt->bit_map_ = temp_map.to_ulong();
	}
    }

    return rpt;
}


void PacketPool::showPool() {
    pkt_iter_t it = backup_queue_.begin();

    cout << "--------size--------- " << backup_queue_.size() << endl;
    for(; it != backup_queue_.end(); it ++) {
	//	cope_inst->qout << setw(10) << Scheduler::instance().clock()
	cout << setw(10) << Scheduler::instance().clock()
	     << setw(8) << HDR_CMN(*it)->uid_
	     << setw(8) << HDR_TCP(*it)->seqno()
	     << setw(8) << HDR_CMN(*it)->cope_pkt_id() 
	     << setw(8) << HDR_CMN(*it)->next_hop();
	if(HDR_CMN(*it)->ptype() == PT_ACK)
	    cout << setw(8) << "ack" << endl;
	else if(HDR_COPE(*it)->ctype_ == COPE_RETX)
	    cout << setw(8) << "crx" << endl;
	else
	    cout << endl;
    }
}
